diff --git a/apps/web/pages/availability/index.tsx b/apps/web/pages/availability/index.tsx
index f8b2b43cb2..4e4738f632 100644
--- a/apps/web/pages/availability/index.tsx
+++ b/apps/web/pages/availability/index.tsx
@@ -2,7 +2,6 @@ import { useAutoAnimate } from "@formkit/auto-animate/react";
import { useRouter, useSearchParams, usePathname } from "next/navigation";
import { useCallback } from "react";
-import { useOrgBranding } from "@calcom/ee/organizations/context/provider";
import { getLayout } from "@calcom/features/MainLayout";
import { NewScheduleButton, ScheduleListItem } from "@calcom/features/schedules";
import { ShellMain } from "@calcom/features/shell/Shell";
@@ -135,7 +134,6 @@ export default function AvailabilityPage() {
const searchParams = useSearchParams();
const router = useRouter();
const pathname = usePathname();
- const orgBranding = useOrgBranding();
// Get a new searchParams string by merging the current
// searchParams with a provided key/value pair
@@ -156,23 +154,22 @@ export default function AvailabilityPage() {
subtitle={t("configure_availability")}
CTA={
- {orgBranding && (
- {
- router.push(`${pathname}?${createQueryString("type", value)}`);
- }}
- options={[
- { value: "mine", label: t("my_availability") },
- { value: "team", label: t("team_availability") },
- ]}
- />
- )}
+ {
+ if (!value) return;
+ router.push(`${pathname}?${createQueryString("type", value)}`);
+ }}
+ options={[
+ { value: "mine", label: t("my_availability") },
+ { value: "team", label: t("team_availability") },
+ ]}
+ />
}>
- {searchParams?.get("type") === "team" && orgBranding ? (
+ {searchParams?.get("type") === "team" ? (
) : (
+
+
+
+
+
+ }>
+ <>>
+
+ );
+}
+
export function AvailabilitySliderTable() {
const tableContainerRef = useRef(null);
const [browsingDate, setBrowsingDate] = useState(dayjs());
@@ -143,6 +172,9 @@ export function AvailabilitySliderTable() {
fetchMoreOnBottomReached(tableContainerRef.current);
}, [fetchMoreOnBottomReached]);
+ // This means they are not apart of any teams so we show the upgrade tip
+ if (!flatData.length) return ;
+
return (
{
- const organizationId = ctx.user.organizationId;
+async function getTeamMembers({
+ teamId,
+ teamIds,
+ cursor,
+ limit,
+}: {
+ teamId?: number;
+ teamIds?: number[];
+ cursor: number | null | undefined;
+ limit: number;
+}) {
+ let whereQuery: Prisma.MembershipWhereInput = {
+ teamId,
+ };
- if (!organizationId) {
- throw new TRPCError({ code: "NOT_FOUND", message: "User is not part of any organization." });
+ if (teamIds) {
+ whereQuery = {
+ teamId: {
+ in: teamIds,
+ },
+ };
}
- const { cursor, limit } = input;
-
- const getTotalMembers = await prisma.membership.count({
+ return await prisma.membership.findMany({
where: {
- teamId: organizationId,
- },
- });
-
- // I couldnt get this query to work direct on membership table
- const teamMembers = await prisma.membership.findMany({
- where: {
- teamId: organizationId,
+ ...whereQuery,
accepted: true,
},
select: {
@@ -54,7 +64,128 @@ export const listTeamAvailabilityHandler = async ({ ctx, input }: GetOptions) =>
orderBy: {
id: "asc",
},
+ distinct: ["userId"],
});
+}
+
+type Member = Awaited>[number];
+
+async function buildMember(member: Member, dateFrom: Dayjs, dateTo: Dayjs) {
+ if (!member.user.defaultScheduleId) {
+ return {
+ id: member.user.id,
+ username: member.user.username,
+ email: member.user.email,
+ timeZone: member.user.timeZone,
+ role: member.role,
+ dateRanges: [] as DateRange[],
+ };
+ }
+
+ const schedule = await prisma.schedule.findUnique({
+ where: { id: member.user.defaultScheduleId },
+ select: { availability: true, timeZone: true },
+ });
+ const timeZone = schedule?.timeZone || member.user.timeZone;
+
+ const dateRanges = buildDateRanges({
+ dateFrom,
+ dateTo,
+ timeZone,
+ availability: schedule?.availability ?? [],
+ });
+
+ return {
+ id: member.user.id,
+ username: member.user.username,
+ email: member.user.email,
+ timeZone,
+ role: member.role,
+ dateRanges,
+ };
+}
+
+async function getInfoForAllTeams({ ctx, input }: GetOptions) {
+ const { cursor, limit } = input;
+
+ // Get all teamIds for the user
+ const teamIds = await prisma.membership
+ .findMany({
+ where: {
+ userId: ctx.user.id,
+ },
+ select: {
+ id: true,
+ },
+ })
+ .then((memberships) => memberships.map((membership) => membership.id));
+
+ if (!teamIds.length) {
+ throw new TRPCError({ code: "NOT_FOUND", message: "User is not part of any organization or team." });
+ }
+
+ const getTotalMembers = await prisma.$queryRaw<{
+ count: number;
+ }>(Prisma.sql`
+ SELECT
+ COUNT(DISTINCT "userId") as "count"
+ FROM "Membership"
+ WHERE "teamId" IN (${Prisma.join(teamIds)})
+`);
+
+ const teamMembers = await getTeamMembers({
+ teamIds,
+ cursor,
+ limit,
+ });
+
+ return {
+ teamMembers,
+ totalTeamMembers: getTotalMembers.count,
+ };
+}
+
+export const listTeamAvailabilityHandler = async ({ ctx, input }: GetOptions) => {
+ const { cursor, limit } = input;
+ const teamId = input.teamId || ctx.user.organizationId;
+
+ let teamMembers: Member[] = [];
+ let totalTeamMembers = 0;
+
+ if (!teamId) {
+ // Get all users TODO:
+ const teamAllInfo = await getInfoForAllTeams({ ctx, input });
+
+ teamMembers = teamAllInfo.teamMembers;
+ totalTeamMembers = teamAllInfo.totalTeamMembers;
+ } else {
+ const isMember = await prisma.membership.findFirst({
+ where: {
+ teamId,
+ userId: ctx.user.id,
+ },
+ });
+
+ if (!isMember) {
+ teamMembers = [];
+ totalTeamMembers = 0;
+ } else {
+ const { cursor, limit } = input;
+
+ totalTeamMembers = await prisma.membership.count({
+ where: {
+ teamId: teamId,
+ },
+ });
+
+ // I couldnt get this query to work direct on membership table
+ teamMembers = await getTeamMembers({
+ teamId,
+ cursor,
+ limit,
+ });
+ }
+ }
let nextCursor: typeof cursor | undefined = undefined;
if (teamMembers && teamMembers.length > limit) {
@@ -65,40 +196,7 @@ export const listTeamAvailabilityHandler = async ({ ctx, input }: GetOptions) =>
const dateFrom = dayjs(input.startDate).tz(input.loggedInUsersTz).subtract(1, "day");
const dateTo = dayjs(input.endDate).tz(input.loggedInUsersTz).add(1, "day");
- const buildMembers = teamMembers?.map(async (member) => {
- if (!member.user.defaultScheduleId) {
- return {
- id: member.user.id,
- username: member.user.username,
- email: member.user.email,
- timeZone: member.user.timeZone,
- role: member.role,
- dateRanges: [] as DateRange[],
- };
- }
-
- const schedule = await prisma.schedule.findUnique({
- where: { id: member.user.defaultScheduleId },
- select: { availability: true, timeZone: true },
- });
- const timeZone = schedule?.timeZone || member.user.timeZone;
-
- const dateRanges = buildDateRanges({
- dateFrom,
- dateTo,
- timeZone,
- availability: schedule?.availability ?? [],
- });
-
- return {
- id: member.user.id,
- username: member.user.username,
- email: member.user.email,
- timeZone,
- role: member.role,
- dateRanges,
- };
- });
+ const buildMembers = teamMembers?.map((member) => buildMember(member, dateFrom, dateTo));
const members = await Promise.all(buildMembers);
@@ -106,7 +204,7 @@ export const listTeamAvailabilityHandler = async ({ ctx, input }: GetOptions) =>
rows: members || [],
nextCursor,
meta: {
- totalRowCount: getTotalMembers || 0,
+ totalRowCount: totalTeamMembers,
},
};
};
diff --git a/packages/trpc/server/routers/viewer/availability/team/listTeamAvailability.schema.ts b/packages/trpc/server/routers/viewer/availability/team/listTeamAvailability.schema.ts
index d07e9401b8..e6e70c63ae 100644
--- a/packages/trpc/server/routers/viewer/availability/team/listTeamAvailability.schema.ts
+++ b/packages/trpc/server/routers/viewer/availability/team/listTeamAvailability.schema.ts
@@ -6,6 +6,7 @@ export const ZListTeamAvailaiblityScheme = z.object({
startDate: z.string(),
endDate: z.string(),
loggedInUsersTz: z.string(),
+ teamId: z.number().optional(),
});
export type TListTeamAvailaiblityScheme = z.infer;