Uh oh!
@@ -84,33 +86,45 @@ export default function User(props): User {
);
}
-export const getServerSideProps: GetServerSideProps = async (context) => {
- const user = await whereAndSelect(
- prisma.user.findFirst,
- {
- username: context.query.user.toLowerCase(),
+export const getServerSideProps = async (context: GetServerSidePropsContext) => {
+ const username = (context.query.user as string).toLowerCase();
+
+ const user = await prisma.user.findUnique({
+ where: {
+ username,
},
- ["id", "username", "email", "name", "bio", "avatar", "theme"]
- );
+ select: {
+ id: true,
+ username: true,
+ email: true,
+ name: true,
+ bio: true,
+ avatar: true,
+ theme: true,
+ plan: true,
+ },
+ });
if (!user) {
return {
notFound: true,
};
}
- const eventTypes = await prisma.eventType.findMany({
+ const eventTypesWithHidden = await prisma.eventType.findMany({
where: {
userId: user.id,
- hidden: false,
},
select: {
+ id: true,
slug: true,
title: true,
length: true,
description: true,
+ hidden: true,
},
+ take: user.plan === "FREE" ? 1 : undefined,
});
-
+ const eventTypes = eventTypesWithHidden.filter((evt) => !evt.hidden);
return {
props: {
user,
diff --git a/pages/[user]/[type].tsx b/pages/[user]/[type].tsx
index f86c838957..02d297523e 100644
--- a/pages/[user]/[type].tsx
+++ b/pages/[user]/[type].tsx
@@ -235,6 +235,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
availability: true,
hideBranding: true,
theme: true,
+ plan: true,
},
});
@@ -243,11 +244,12 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
notFound: true,
} as const;
}
-
- const eventType = await prisma.eventType.findFirst({
+ const eventType = await prisma.eventType.findUnique({
where: {
- userId: user.id,
- slug: typeParam,
+ userId_slug: {
+ userId: user.id,
+ slug: typeParam,
+ },
},
select: {
id: true,
@@ -262,15 +264,32 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
periodEndDate: true,
periodCountCalendarDays: true,
minimumBookingNotice: true,
+ hidden: true,
},
});
- if (!eventType) {
+ if (!eventType || eventType.hidden) {
return {
notFound: true,
} as const;
}
+ // check this is the first event
+ if (user.plan === "FREE") {
+ const firstEventType = await prisma.eventType.findFirst({
+ where: {
+ userId: user.id,
+ },
+ select: {
+ id: true,
+ },
+ });
+ if (firstEventType?.id !== eventType.id) {
+ return {
+ notFound: true,
+ } as const;
+ }
+ }
const getWorkingHours = (providesAvailability: { availability: Availability[] }) =>
providesAvailability.availability && providesAvailability.availability.length
? providesAvailability.availability
diff --git a/pages/event-types/[type].tsx b/pages/event-types/[type].tsx
index bd5c35a354..8b88d7a440 100644
--- a/pages/event-types/[type].tsx
+++ b/pages/event-types/[type].tsx
@@ -1,8 +1,7 @@
-import Link from "next/link";
import { useRouter } from "next/router";
import Modal from "@components/Modal";
import React, { useEffect, useRef, useState } from "react";
-import Select, { OptionBase } from "react-select";
+import Select, { OptionTypeBase } from "react-select";
import prisma from "@lib/prisma";
import { EventTypeCustomInput, EventTypeCustomInputType } from "@prisma/client";
import { LocationType } from "@lib/location";
@@ -25,7 +24,7 @@ import {
import dayjs from "dayjs";
import utc from "dayjs/plugin/utc";
import timezone from "dayjs/plugin/timezone";
-import { Availability, EventType, User } from "@prisma/client";
+import { Availability } from "@prisma/client";
import { validJson } from "@lib/jsonUtils";
import classnames from "classnames";
import throttle from "lodash.throttle";
@@ -42,6 +41,8 @@ import updateEventType from "@lib/mutations/event-types/update-event-type";
import deleteEventType from "@lib/mutations/event-types/delete-event-type";
import showToast from "@lib/notification";
import { inferSSRProps } from "@lib/types/inferSSRProps";
+import { asStringOrThrow } from "@lib/asStringOrNull";
+import Button from "@components/ui/Button";
dayjs.extend(utc);
dayjs.extend(timezone);
@@ -66,7 +67,7 @@ const EventTypePage = (props: inferSSRProps
) => {
const router = useRouter();
const [successModalOpen, setSuccessModalOpen] = useState(false);
- const inputOptions: OptionBase[] = [
+ const inputOptions: OptionTypeBase[] = [
{ value: EventTypeCustomInputType.TEXT, label: "Text" },
{ value: EventTypeCustomInputType.TEXTLONG, label: "Multiline Text" },
{ value: EventTypeCustomInputType.NUMBER, label: "Number" },
@@ -130,8 +131,8 @@ const EventTypePage = (props: inferSSRProps) => {
const [showLocationModal, setShowLocationModal] = useState(false);
const [showAddCustomModal, setShowAddCustomModal] = useState(false);
const [selectedTimeZone, setSelectedTimeZone] = useState("");
- const [selectedLocation, setSelectedLocation] = useState(undefined);
- const [selectedInputOption, setSelectedInputOption] = useState(inputOptions[0]);
+ const [selectedLocation, setSelectedLocation] = useState(undefined);
+ const [selectedInputOption, setSelectedInputOption] = useState(inputOptions[0]);
const [locations, setLocations] = useState(eventType.locations || []);
const [selectedCustomInput, setSelectedCustomInput] = useState(undefined);
const [customInputs, setCustomInputs] = useState(
@@ -162,14 +163,14 @@ const EventTypePage = (props: inferSSRProps) => {
});
const [hidden, setHidden] = useState(eventType.hidden);
- const titleRef = useRef();
- const slugRef = useRef();
- const descriptionRef = useRef();
- const lengthRef = useRef();
- const requiresConfirmationRef = useRef();
- const eventNameRef = useRef();
- const periodDaysRef = useRef();
- const periodDaysTypeRef = useRef();
+ const titleRef = useRef(null);
+ const slugRef = useRef(null);
+ const descriptionRef = useRef(null);
+ const lengthRef = useRef(null);
+ const requiresConfirmationRef = useRef(null);
+ const eventNameRef = useRef(null);
+ const periodDaysRef = useRef(null);
+ const periodDaysTypeRef = useRef(null);
useEffect(() => {
setSelectedTimeZone(eventType.timeZone || user.timeZone);
@@ -804,17 +805,11 @@ const EventTypePage = (props: inferSSRProps) => {
>
)}
-
-
-
- Cancel
-
-
-
+
+
+
) => {
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
const { req, query } = context;
const session = await getSession({ req });
- if (!session) {
+ const typeParam = asStringOrThrow(query.type);
+
+ if (!session?.user?.id) {
return {
redirect: {
permanent: false,
@@ -1042,22 +1039,38 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
};
}
- const user: User = await prisma.user.findFirst({
+ const user = await prisma.user.findUnique({
where: {
- email: session.user.email,
+ id: session.user.id,
},
select: {
+ id: true,
username: true,
timeZone: true,
startTime: true,
endTime: true,
availability: true,
+ plan: true,
},
});
- const eventType: EventType | null = await prisma.eventType.findUnique({
+ if (!user) {
+ return {
+ notFound: true,
+ } as const;
+ }
+
+ const eventType = await prisma.eventType.findFirst({
where: {
- id: parseInt(query.type as string),
+ userId: user.id,
+ OR: [
+ {
+ slug: typeParam,
+ },
+ {
+ id: parseInt(typeParam),
+ },
+ ],
},
select: {
id: true,
@@ -1116,10 +1129,10 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
},
];
- const locationOptions: OptionBase[] = [
+ const locationOptions: OptionTypeBase[] = [
{ value: LocationType.InPerson, label: "In-person meeting" },
{ value: LocationType.Phone, label: "Phone call" },
- { value: LocationType.Zoom, label: "Zoom Video" },
+ { value: LocationType.Zoom, label: "Zoom Video", disabled: true },
];
const hasGoogleCalendarIntegration = integrations.find(
diff --git a/pages/event-types/index.tsx b/pages/event-types/index.tsx
index b133947fe6..2dde812bc9 100644
--- a/pages/event-types/index.tsx
+++ b/pages/event-types/index.tsx
@@ -1,4 +1,4 @@
-import { Dialog, DialogClose, DialogContent } from "@components/Dialog";
+import { Dialog, DialogContent } from "@components/Dialog";
import Loader from "@components/Loader";
import { Tooltip } from "@components/Tooltip";
import { Button } from "@components/ui/Button";
@@ -21,81 +21,57 @@ import { useRouter } from "next/router";
import React, { Fragment, useRef } from "react";
import Shell from "@components/Shell";
import prisma from "@lib/prisma";
-import { GetServerSidePropsContext, InferGetServerSidePropsType } from "next";
+import { GetServerSidePropsContext } from "next";
import { useMutation } from "react-query";
import createEventType from "@lib/mutations/event-types/create-event-type";
-import { ONBOARDING_INTRODUCED_AT } from "@lib/getting-started";
import { getSession } from "@lib/auth";
+import { ONBOARDING_INTRODUCED_AT } from "@lib/getting-started";
+import { useToggleQuery } from "@lib/hooks/useToggleQuery";
+import { inferSSRProps } from "@lib/types/inferSSRProps";
+import { Alert } from "@components/ui/Alert";
-const EventTypesPage = (props: InferGetServerSidePropsType) => {
+const EventTypesPage = (props: inferSSRProps) => {
const { user, types } = props;
const [session, loading] = useSession();
const router = useRouter();
const createMutation = useMutation(createEventType, {
onSuccess: async ({ eventType }) => {
- await router.replace("/event-types/" + eventType.id);
+ await router.push("/event-types/" + eventType.id);
showToast(`${eventType.title} event type created successfully`, "success");
},
onError: (err: Error) => {
showToast(err.message, "error");
},
});
+ const modalOpen = useToggleQuery("new");
- const titleRef = useRef();
- const slugRef = useRef();
- const descriptionRef = useRef();
- const lengthRef = useRef();
-
- const dialogOpen = router.query.new === "1";
-
- async function createEventTypeHandler(event) {
- event.preventDefault();
-
- const enteredTitle = titleRef.current.value;
- const enteredSlug = slugRef.current.value;
- const enteredDescription = descriptionRef.current.value;
- const enteredLength = parseInt(lengthRef.current.value);
-
- const body = {
- title: enteredTitle,
- slug: enteredSlug,
- description: enteredDescription,
- length: enteredLength,
- };
-
- createMutation.mutate(body);
- }
-
- function autoPopulateSlug() {
- let t = titleRef.current.value;
- t = t.replace(/\s+/g, "-").toLowerCase();
- slugRef.current.value = t;
- }
+ const slugRef = useRef(null);
if (loading) {
return ;
}
- const CreateNewEventDialog = () => (
+ const renderEventDialog = () => (