Merge branch 'main' into add_booking_confirmed_webhook_event

add_booking_confirmed_webhook_event
kodiakhq[bot] 2022-01-11 10:33:18 +00:00 committed by GitHub
commit b000b970b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 86 additions and 19 deletions

View File

@ -8,6 +8,7 @@ import {
LinkIcon,
LogoutIcon,
PuzzleIcon,
MoonIcon,
} from "@heroicons/react/solid";
import { signOut, useSession } from "next-auth/react";
import Link from "next/link";
@ -356,14 +357,25 @@ function UserDropdown({ small }: { small?: boolean }) {
const { t } = useLocale();
const query = useMeQuery();
const user = query.data;
const mutation = trpc.useMutation("viewer.away");
const utils = trpc.useContext();
return (
<Dropdown>
<DropdownMenuTrigger asChild>
<div className="flex items-center w-full space-x-2 cursor-pointer group">
<span
className={classNames(small ? "w-8 h-8" : "w-10 h-10", "bg-gray-300 rounded-full flex-shrink-0")}>
className={classNames(
small ? "w-8 h-8" : "w-10 h-10",
"bg-gray-300 rounded-full flex-shrink-0 relative"
)}>
<Avatar imageSrc={user?.avatar || ""} alt={user?.username || "Nameless User"} />
{!user?.away && (
<div className="absolute bottom-0 right-0 w-3 h-3 bg-green-500 border-2 border-white rounded-full"></div>
)}
{user?.away && (
<div className="absolute bottom-0 right-0 w-3 h-3 bg-yellow-500 border-2 border-white rounded-full"></div>
)}
</span>
{!small && (
<span className="flex items-center flex-grow truncate">
@ -384,6 +396,26 @@ function UserDropdown({ small }: { small?: boolean }) {
</div>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>
<a
onClick={() => {
mutation.mutate({ away: !user?.away });
utils.invalidateQueries("viewer.me");
}}
className="flex px-4 py-2 text-sm cursor-pointer hover:bg-gray-100 hover:text-gray-900">
<MoonIcon
className={classNames(
user?.away
? "text-purple-500 group-hover:text-purple-700"
: "text-gray-500 group-hover:text-gray-700",
"mr-2 flex-shrink-0 h-5 w-5"
)}
aria-hidden="true"
/>
{user?.away ? t("set_as_free") : t("set_as_away")}
</a>
</DropdownMenuItem>
<DropdownMenuSeparator className="h-px bg-gray-200" />
{user?.username && (
<DropdownMenuItem>
<a

View File

@ -26,7 +26,7 @@ export const DropdownMenuContent = forwardRef<HTMLDivElement, DropdownMenuConten
return (
<DropdownMenuPrimitive.Content
{...props}
className="z-10 mt-1 text-sm origin-top-right bg-white rounded-sm shadow-lg w-44 ring-1 ring-black ring-opacity-5 focus:outline-none"
className="z-10 w-48 mt-1 text-sm origin-top-right bg-white rounded-sm shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"
ref={forwardedRef}>
{children}
</DropdownMenuPrimitive.Content>

View File

@ -1,4 +1,5 @@
import { ArrowRightIcon } from "@heroicons/react/outline";
import { MoonIcon } from "@heroicons/react/solid";
import { GetServerSidePropsContext } from "next";
import Link from "next/link";
import { useRouter } from "next/router";
@ -49,23 +50,31 @@ export default function User(props: inferSSRProps<typeof getServerSideProps>) {
<p className="text-neutral-500 dark:text-white">{user.bio}</p>
</div>
<div className="space-y-6" data-testid="event-types">
{eventTypes.map((type) => (
<div
key={type.id}
className="relative bg-white border rounded-sm group dark:bg-neutral-900 dark:border-0 dark:hover:border-neutral-600 hover:bg-gray-50 border-neutral-200 hover:border-brand">
<ArrowRightIcon className="absolute w-4 h-4 text-black transition-opacity opacity-0 right-3 top-3 dark:text-white group-hover:opacity-100" />
<Link
href={{
pathname: `/${user.username}/${type.slug}`,
query,
}}>
<a className="block px-6 py-4" data-testid="event-type-link">
<h2 className="font-semibold text-neutral-900 dark:text-white">{type.title}</h2>
<EventTypeDescription eventType={type} />
</a>
</Link>
{user.away && (
<div className="relative px-6 py-4 bg-white border rounded-sm group dark:bg-neutral-900 dark:border-0 border-neutral-200">
<MoonIcon className="w-8 h-8 mb-4 text-neutral-800" />
<h2 className="font-semibold text-neutral-900 dark:text-white">{t("user_away")}</h2>
<p className="text-neutral-500 dark:text-white">{t("user_away_description")}</p>
</div>
))}
)}
{!user.away &&
eventTypes.map((type) => (
<div
key={type.id}
className="relative bg-white border rounded-sm group dark:bg-neutral-900 dark:border-0 dark:hover:border-neutral-600 hover:bg-gray-50 border-neutral-200 hover:border-brand">
<ArrowRightIcon className="absolute w-4 h-4 text-black transition-opacity opacity-0 right-3 top-3 dark:text-white group-hover:opacity-100" />
<Link
href={{
pathname: `/${user.username}/${type.slug}`,
query,
}}>
<a className="block px-6 py-4" data-testid="event-type-link">
<h2 className="font-semibold text-neutral-900 dark:text-white">{type.title}</h2>
<EventTypeDescription eventType={type} />
</a>
</Link>
</div>
))}
</div>
{eventTypes.length === 0 && (
<div className="overflow-hidden rounded-sm shadow">
@ -102,6 +111,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
avatar: true,
theme: true,
plan: true,
away: true,
},
});

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "users" ADD COLUMN "away" BOOLEAN NOT NULL DEFAULT false;

View File

@ -118,6 +118,7 @@ model User {
brandColor String @default("#292929")
// the location where the events will end up
destinationCalendar DestinationCalendar?
away Boolean @default(false)
metadata Json?
@@map(name: "users")

View File

@ -570,5 +570,9 @@
"error_required_field": "This field is required.",
"status": "Status",
"team_view_user_availability": "View user availability",
"team_view_user_availability_disabled": "User needs to accept invite to view availability"
"team_view_user_availability_disabled": "User needs to accept invite to view availability",
"set_as_away": "Set yourself as away",
"set_as_free": "Disable away status",
"user_away": "This user is currently away.",
"user_away_description": "The person you are trying to book has set themselves to away, and therefore is not accepting new bookings."
}

View File

@ -45,6 +45,7 @@ async function getUserFromSession({
twoFactorEnabled: true,
brandColor: true,
plan: true,
away: true,
credentials: {
select: {
id: true,

View File

@ -57,6 +57,7 @@ const loggedInViewerRouter = createProtectedRouter()
twoFactorEnabled,
brandColor,
plan,
away,
} = ctx.user;
const me = {
id,
@ -73,10 +74,26 @@ const loggedInViewerRouter = createProtectedRouter()
twoFactorEnabled,
brandColor,
plan,
away,
};
return me;
},
})
.mutation("away", {
input: z.object({
away: z.boolean(),
}),
async resolve({ input, ctx }) {
await ctx.prisma.user.update({
where: {
email: ctx.user.email,
},
data: {
away: input.away,
},
});
},
})
.query("eventTypes", {
async resolve({ ctx }) {
const { prisma } = ctx;