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;