+ {teamId && (
+
+
+
+
+ Collective
+ Schedule meetings when all selected team members are available.
+
+
+ Round Robin
+ Cycle meetings between multiple team members.
+
+
+
+ )}
+
-
+
);
-
- return (
-
-
- {props.user.plan === "FREE" && (
- You need to upgrade your plan to have more than one active event type.>}
- message={
- <>
- To upgrade go to{" "}
-
- calendso.com/upgrade
-
- >
- }
- className="my-4"
- />
- )}
-
-
- {types.map((item) => (
- -
-
-
-
-
-
-
-
{item.title}
- {item.hidden && (
-
- Hidden
-
- )}
-
-
-
-
- {item.description && (
-
-
-
- {item.description.substring(0, 100)}
-
-
- )}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ))}
-
-
- {types.length === 0 && (
-
-
-
-
Create your first event type
-
- Event types enable you to share links that show available times on your calendar and allow
- people to make bookings with you.
-
- {renderEventDialog()}
-
-
- )}
-
-
- );
};
-export const getServerSideProps = async (context: GetServerSidePropsContext) => {
- const { req } = context;
- const session = await getSession({ req });
-
- if (!session?.user?.id) {
- return {
- redirect: {
- permanent: false,
- destination: "/auth/login",
- },
- } as const;
+export async function getServerSideProps(context) {
+ const session = await getSession(context);
+ if (!session) {
+ return { redirect: { permanent: false, destination: "/auth/login" } };
}
const user = await prisma.user.findUnique({
@@ -683,12 +510,74 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
select: {
id: true,
username: true,
+ name: true,
startTime: true,
endTime: true,
bufferTime: true,
+ avatar: true,
completedOnboarding: true,
createdDate: true,
plan: true,
+ teams: {
+ where: {
+ accepted: true,
+ },
+ select: {
+ role: true,
+ team: {
+ select: {
+ id: true,
+ name: true,
+ slug: true,
+ logo: true,
+ members: {
+ select: {
+ userId: true,
+ },
+ },
+ eventTypes: {
+ select: {
+ id: true,
+ title: true,
+ description: true,
+ length: true,
+ schedulingType: true,
+ slug: true,
+ hidden: true,
+ users: {
+ select: {
+ id: true,
+ avatar: true,
+ name: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ eventTypes: {
+ where: {
+ team: null,
+ },
+ select: {
+ id: true,
+ title: true,
+ description: true,
+ length: true,
+ schedulingType: true,
+ slug: true,
+ hidden: true,
+ users: {
+ select: {
+ id: true,
+ avatar: true,
+ name: true,
+ },
+ },
+ },
+ },
},
});
@@ -711,9 +600,12 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
} as const;
}
+ let eventTypes = [];
+
+ // backwards compatibility, TMP:
const typesRaw = await prisma.eventType.findMany({
where: {
- userId: user.id,
+ userId: session.user.id,
},
select: {
id: true,
@@ -722,34 +614,73 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
description: true,
length: true,
hidden: true,
+ users: {
+ select: {
+ id: true,
+ avatar: true,
+ name: true,
+ },
+ },
},
});
- const types = typesRaw.map((type, index) =>
- user.plan === "FREE" && index > 0
- ? {
- ...type,
- $disabled: true,
- }
- : {
- ...type,
- $disabled: false,
- }
+ eventTypes.push({
+ teamId: null,
+ profile: {
+ slug: user.username,
+ name: user.name,
+ image: user.avatar,
+ },
+ eventTypes: user.eventTypes.concat(typesRaw).map((type, index) =>
+ user.plan === "FREE" && index > 0
+ ? {
+ ...type,
+ $disabled: true,
+ }
+ : {
+ ...type,
+ $disabled: false,
+ }
+ ),
+ });
+
+ eventTypes = [].concat(
+ eventTypes,
+ user.teams.map((membership) => ({
+ teamId: membership.team.id,
+ profile: {
+ name: membership.team.name,
+ image: membership.team.logo || "",
+ slug: "team/" + membership.team.slug,
+ },
+ metadata: {
+ membershipCount: membership.team.members.length,
+ readOnly: membership.role !== "OWNER",
+ },
+ eventTypes: membership.team.eventTypes,
+ }))
);
const userObj = Object.assign({}, user, {
createdDate: user.createdDate.toString(),
});
- const canAddEvents = user.plan !== "FREE" || types.length < 1;
+ const canAddEvents = user.plan !== "FREE" || eventTypes.length < 1;
return {
props: {
- user: userObj,
- types,
canAddEvents,
+ user: userObj,
+ // don't display event teams without event types,
+ eventTypes: eventTypes.filter((groupBy) => groupBy.eventTypes.length > 0),
+ // so we can show a dropdown when the user has teams
+ profiles: eventTypes.map((group) => ({
+ teamId: group.teamId,
+ ...group.profile,
+ ...group.metadata,
+ })),
},
};
-};
+}
export default EventTypesPage;
diff --git a/pages/reschedule/[uid].tsx b/pages/reschedule/[uid].tsx
index 87cfe4916a..9decdaacc7 100644
--- a/pages/reschedule/[uid].tsx
+++ b/pages/reschedule/[uid].tsx
@@ -1,5 +1,6 @@
import { GetServerSidePropsContext } from "next";
-import prisma from "../../lib/prisma";
+import prisma from "@lib/prisma";
+import { asStringOrNull } from "@lib/asStringOrNull";
export default function Type() {
// Just redirect to the schedule page to reschedule it.
@@ -7,14 +8,28 @@ export default function Type() {
}
export async function getServerSideProps(context: GetServerSidePropsContext) {
- const booking = await prisma.booking.findFirst({
+ const booking = await prisma.booking.findUnique({
where: {
- uid: context.query.uid as string,
+ uid: asStringOrNull(context.query.uid),
},
select: {
id: true,
- user: { select: { username: true } },
- eventType: { select: { slug: true } },
+ eventType: {
+ select: {
+ users: {
+ select: {
+ username: true,
+ },
+ },
+ slug: true,
+ team: {
+ select: {
+ slug: true,
+ },
+ },
+ },
+ },
+ user: true,
title: true,
description: true,
startTime: true,
@@ -22,16 +37,21 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
attendees: true,
},
});
- if (!booking?.user || !booking.eventType) {
+
+ if (!booking.eventType) {
return {
notFound: true,
};
}
+ const eventType = booking.eventType;
+
+ const eventPage =
+ (eventType.team ? "team/" + eventType.team.slug : booking.user.username) + "/" + booking.eventType.slug;
+
return {
redirect: {
- destination:
- "/" + booking.user.username + "/" + booking.eventType.slug + "?rescheduleUid=" + context.query.uid,
+ destination: "/" + eventPage + "?rescheduleUid=" + context.query.uid,
permanent: false,
},
};
diff --git a/pages/sandbox/RadioArea.tsx b/pages/sandbox/RadioArea.tsx
new file mode 100644
index 0000000000..2dd4617517
--- /dev/null
+++ b/pages/sandbox/RadioArea.tsx
@@ -0,0 +1,103 @@
+import * as RadioArea from "@components/ui/form/radio-area";
+import Head from "next/head";
+import React, { useState } from "react";
+
+const selectOptions = [
+ {
+ value: "rabbit",
+ label: "Rabbit",
+ description: "Fast and hard.",
+ },
+ {
+ value: "turtle",
+ label: "Turtle",
+ description: "Slow and steady.",
+ },
+];
+
+export default function RadioAreaPage() {
+ const [formData, setFormData] = useState({});
+
+ const onSubmit = (e) => {
+ e.preventDefault();
+ };
+
+ return (
+ <>
+
+
+
+
+
RadioArea component
+
+
{JSON.stringify(formData)}
+
+ >
+ );
+}
diff --git a/pages/settings/profile.tsx b/pages/settings/profile.tsx
index 79ef270110..3960ee506a 100644
--- a/pages/settings/profile.tsx
+++ b/pages/settings/profile.tsx
@@ -4,7 +4,7 @@ import prisma from "@lib/prisma";
import Modal from "@components/Modal";
import Shell from "@components/Shell";
import SettingsShell from "@components/Settings";
-import Avatar from "@components/Avatar";
+import Avatar from "@components/ui/Avatar";
import { getSession } from "@lib/auth";
import Select from "react-select";
import TimezoneSelect from "react-timezone-select";
diff --git a/pages/success.tsx b/pages/success.tsx
index a986f10c0e..e8a311ca1f 100644
--- a/pages/success.tsx
+++ b/pages/success.tsx
@@ -11,11 +11,11 @@ import toArray from "dayjs/plugin/toArray";
import timezone from "dayjs/plugin/timezone";
import { createEvent } from "ics";
import { getEventName } from "@lib/event";
-import Theme from "@components/Theme";
-import { GetServerSidePropsContext } from "next";
-import { asStringOrNull } from "../lib/asStringOrNull";
+import useTheme from "@lib/hooks/useTheme";
+import { asStringOrNull } from "@lib/asStringOrNull";
import { inferSSRProps } from "@lib/types/inferSSRProps";
import { isBrandingHidden } from "@lib/isBrandingHidden";
+import { EventType } from "@prisma/client";
dayjs.extend(utc);
dayjs.extend(toArray);
@@ -26,8 +26,8 @@ export default function Success(props: inferSSRProps
)
const { location, name } = router.query;
const [is24h, setIs24h] = useState(false);
- const [date, setDate] = useState(dayjs.utc(router.query.date));
- const { isReady } = Theme(props.user.theme);
+ const [date, setDate] = useState(dayjs.utc(asStringOrNull(router.query.date)));
+ const { isReady } = useTheme(props.profile.theme);
useEffect(() => {
setDate(date.tz(localStorage.getItem("timeOption.preferredTimeZone") || dayjs.tz.guess()));
@@ -99,9 +99,9 @@ export default function Success(props: inferSSRProps)
{props.eventType.requiresConfirmation
- ? `${
- props.user.name || props.user.username
- } still needs to confirm or reject the booking.`
+ ? props.profile.name !== null
+ ? `${props.profile.name} still needs to confirm or reject the booking.`
+ : "Your booking still needs to be confirmed or rejected."
: `We emailed you and the other attendees a calendar invitation with all the details.`}
@@ -224,7 +224,7 @@ export default function Success(props: inferSSRProps)