new EmptyScreen component, using it in /bookings (#708)

* new EmptyScreen component, using it in /bookings

* Linting fixes

Co-authored-by: Bailey Pumfleet <pumfleet@hey.com>
This commit is contained in:
Peer Richelsen 2021-09-21 11:36:29 +01:00 committed by GitHub
parent 96fd7ecf41
commit 3c089af58a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 303 additions and 261 deletions

View file

@ -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 (
<>
<div className="min-h-80 border border-dashed rounded-sm flex justify-center items-center flex-col">
<div className="bg-white w-[72px] h-[72px] flex justify-center items-center rounded-full">
<Icon className="inline-block w-10 h-10 bg-white" />
</div>
<div className="max-w-[500px] text-center">
<h2 className="font-cal text-xl leading-7 font-bold mt-6 mb-3">{headline}</h2>
<p className="leading-6">{description}</p>
</div>
</div>
</>
);
}

View file

@ -1,9 +1,8 @@
import classNames from "@lib/classNames"; import classNames from "@lib/classNames";
import { SVGComponent } from "@lib/types/SVGComponent";
import Link, { LinkProps } from "next/link"; import Link, { LinkProps } from "next/link";
import React, { forwardRef } from "react"; import React, { forwardRef } from "react";
type SVGComponent = React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
export type ButtonProps = { export type ButtonProps = {
color?: "primary" | "secondary" | "minimal" | "warn"; color?: "primary" | "secondary" | "minimal" | "warn";
size?: "base" | "sm" | "lg" | "fab"; size?: "base" | "sm" | "lg" | "fab";

View file

@ -59,7 +59,8 @@ export default class EventAttendeeMail extends EventMail {
</tr> </tr>
<tr> <tr>
<td>When</td> <td>When</td>
<td>${this.getInviteeStart().format("dddd, LL")}<br>${this.getInviteeStart().format("h:mma")} (${this.calEvent.attendees[0].timeZone <td>${this.getInviteeStart().format("dddd, LL")}<br>${this.getInviteeStart().format("h:mma")} (${
this.calEvent.attendees[0].timeZone
})</td> })</td>
</tr> </tr>
<tr> <tr>
@ -141,7 +142,8 @@ export default class EventAttendeeMail extends EventMail {
to: `${this.calEvent.attendees[0].name} <${this.calEvent.attendees[0].email}>`, to: `${this.calEvent.attendees[0].name} <${this.calEvent.attendees[0].email}>`,
from: `${this.calEvent.organizer.name} <${this.getMailerOptions().from}>`, from: `${this.calEvent.organizer.name} <${this.getMailerOptions().from}>`,
replyTo: this.calEvent.organizer.email, replyTo: this.calEvent.organizer.email,
subject: `Confirmed: ${this.calEvent.type} with ${this.calEvent.team?.name || this.calEvent.organizer.name subject: `Confirmed: ${this.calEvent.type} with ${
this.calEvent.team?.name || this.calEvent.organizer.name
} on ${this.getInviteeStart().format("LT dddd, LL")}`, } on ${this.getInviteeStart().format("LT dddd, LL")}`,
html: this.getHtmlRepresentation(), html: this.getHtmlRepresentation(),
text: this.getPlainTextRepresentation(), text: this.getPlainTextRepresentation(),

View file

@ -51,7 +51,8 @@ export default class EventOrganizerMail extends EventMail {
} }
protected getAdditionalFooter(): string { protected getAdditionalFooter(): string {
return `<p style="color: #4b5563; margin-top: 20px;">Need to make a change? <a href=${process.env.BASE_URL + "/bookings" return `<p style="color: #4b5563; margin-top: 20px;">Need to make a change? <a href=${
process.env.BASE_URL + "/bookings"
} style="color: #161e2e;">Manage my bookings</a></p>`; } style="color: #161e2e;">Manage my bookings</a></p>`;
} }
@ -107,12 +108,14 @@ export default class EventOrganizerMail extends EventMail {
</tr> </tr>
<tr> <tr>
<td>When</td> <td>When</td>
<td>${this.getOrganizerStart().format("dddd, LL")}<br>${this.getOrganizerStart().format("h:mma")} (${this.calEvent.organizer.timeZone <td>${this.getOrganizerStart().format("dddd, LL")}<br>${this.getOrganizerStart().format("h:mma")} (${
this.calEvent.organizer.timeZone
})</td> })</td>
</tr> </tr>
<tr> <tr>
<td>Who</td> <td>Who</td>
<td>${this.calEvent.attendees[0].name}<br /><small><a href="mailto:${this.calEvent.attendees[0].email <td>${this.calEvent.attendees[0].name}<br /><small><a href="mailto:${
this.calEvent.attendees[0].email
}">${this.calEvent.attendees[0].email}</a></small></td> }">${this.calEvent.attendees[0].email}</a></small></td>
</tr> </tr>
<tr> <tr>

View file

@ -68,7 +68,8 @@ export default class EventRejectionMail extends EventMail {
to: `${this.calEvent.attendees[0].name} <${this.calEvent.attendees[0].email}>`, to: `${this.calEvent.attendees[0].name} <${this.calEvent.attendees[0].email}>`,
from: `${this.calEvent.organizer.name} <${this.getMailerOptions().from}>`, from: `${this.calEvent.organizer.name} <${this.getMailerOptions().from}>`,
replyTo: this.calEvent.organizer.email, replyTo: this.calEvent.organizer.email,
subject: `Rejected: ${this.calEvent.type} with ${this.calEvent.organizer.name subject: `Rejected: ${this.calEvent.type} with ${
this.calEvent.organizer.name
} on ${this.getInviteeStart().format("dddd, LL")}`, } on ${this.getInviteeStart().format("dddd, LL")}`,
html: this.getHtmlRepresentation(), html: this.getHtmlRepresentation(),
text: this.getPlainTextRepresentation(), text: this.getPlainTextRepresentation(),

View file

@ -0,0 +1 @@
export type SVGComponent = React.FunctionComponent<React.SVGProps<SVGSVGElement>>;

View file

@ -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" 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"
/> />
<a <a
onClick={() => onClick={() => signIn("Cal.com", { callbackUrl: (router.query.callbackUrl || "") as string })}
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"> 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 Login instead
</a> </a>

View file

@ -8,16 +8,19 @@ import { Fragment } from "react";
import { Menu, Transition } from "@headlessui/react"; import { Menu, Transition } from "@headlessui/react";
import { DotsHorizontalIcon } from "@heroicons/react/solid"; import { DotsHorizontalIcon } from "@heroicons/react/solid";
import classNames from "@lib/classNames"; 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 Loader from "@components/Loader";
import { Button } from "@components/ui/Button"; import { Button } from "@components/ui/Button";
import { getSession } from "@lib/auth"; import { getSession } from "@lib/auth";
import { BookingStatus, User } from "@prisma/client"; import { BookingStatus, User } from "@prisma/client";
import EmptyScreen from "@components/EmptyScreen";
export default function Bookings({ bookings }) { export default function Bookings({ bookings }) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
const [session, loading] = useSession(); const [session, loading] = useSession();
const isEmpty = Object.keys(bookings).length === 0;
const router = useRouter(); const router = useRouter();
if (loading) { if (loading) {
@ -43,6 +46,13 @@ export default function Bookings({ bookings }) {
<div className="-mx-4 sm:mx-auto flex flex-col"> <div className="-mx-4 sm:mx-auto flex flex-col">
<div className="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8"> <div className="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div className="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8"> <div className="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">
{isEmpty ? (
<EmptyScreen
Icon={CalendarIcon}
headline="No upcoming bookings, yet"
description="You have no upcoming bookings. As soon as someone books a time with you it will show up here."
/>
) : (
<div className="border border-gray-200 overflow-hidden border-b rounded-sm"> <div className="border border-gray-200 overflow-hidden border-b rounded-sm">
<table className="min-w-full divide-y divide-gray-200"> <table className="min-w-full divide-y divide-gray-200">
<tbody className="bg-white divide-y divide-gray-200" data-testid="bookings"> <tbody className="bg-white divide-y divide-gray-200" data-testid="bookings">
@ -264,6 +274,7 @@ export default function Bookings({ bookings }) {
</tbody> </tbody>
</table> </table>
</div> </div>
)}
</div> </div>
</div> </div>
</div> </div>

View file

@ -137,7 +137,8 @@ export default function Success(props: inferSSRProps<typeof getServerSideProps>)
.format("YYYYMMDDTHHmmss[Z]")}/${date .format("YYYYMMDDTHHmmss[Z]")}/${date
.add(props.eventType.length, "minute") .add(props.eventType.length, "minute")
.utc() .utc()
.format("YYYYMMDDTHHmmss[Z]")}&text=${eventName}&details=${props.eventType.description .format("YYYYMMDDTHHmmss[Z]")}&text=${eventName}&details=${
props.eventType.description
}` + (location ? "&location=" + encodeURIComponent(location) : "") }` + (location ? "&location=" + encodeURIComponent(location) : "")
}> }>
<a className="mx-2 rounded-sm border border-neutral-200 dark:border-neutral-700 dark:text-white py-2 px-3"> <a className="mx-2 rounded-sm border border-neutral-200 dark:border-neutral-700 dark:text-white py-2 px-3">