diff --git a/packages/features/timezone-buddy/components/AvailabilitySliderTable.tsx b/packages/features/timezone-buddy/components/AvailabilitySliderTable.tsx index 4b49fdb79d..979e150438 100644 --- a/packages/features/timezone-buddy/components/AvailabilitySliderTable.tsx +++ b/packages/features/timezone-buddy/components/AvailabilitySliderTable.tsx @@ -1,3 +1,5 @@ +"use client"; + import { keepPreviousData } from "@tanstack/react-query"; import type { ColumnDef } from "@tanstack/react-table"; import { getCoreRowModel, getFilteredRowModel, useReactTable } from "@tanstack/react-table"; @@ -6,6 +8,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import dayjs from "@calcom/dayjs"; import { APP_NAME, WEBAPP_URL } from "@calcom/lib/constants"; import type { DateRange } from "@calcom/lib/date-ranges"; +import { useDebounce } from "@calcom/lib/hooks/useDebounce"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import type { MembershipRole } from "@calcom/prisma/enums"; import { trpc } from "@calcom/trpc"; @@ -64,6 +67,8 @@ export function AvailabilitySliderTable(props: { userTimeFormat: number | null; const [browsingDate, setBrowsingDate] = useState(dayjs()); const [editSheetOpen, setEditSheetOpen] = useState(false); const [selectedUser, setSelectedUser] = useState(null); + const [searchString, setSearchString] = useState(""); + const debouncedSearchString = useDebounce(searchString, 500); const tbStore = createTimezoneBuddyStore({ browsingDate: browsingDate.toDate(), @@ -75,6 +80,7 @@ export function AvailabilitySliderTable(props: { userTimeFormat: number | null; loggedInUsersTz: dayjs.tz.guess() || "Europe/London", startDate: browsingDate.startOf("day").toISOString(), endDate: browsingDate.endOf("day").toISOString(), + searchString: debouncedSearchString, }, { getNextPageParam: (lastPage) => lastPage.nextCursor, @@ -202,7 +208,7 @@ export function AvailabilitySliderTable(props: { userTimeFormat: number | null; }); // This means they are not apart of any teams so we show the upgrade tip - if (!flatData.length) return ; + if (!flatData.length && !data?.pages?.[0]?.meta?.isApartOfAnyTeam) return ; return ( fetchMoreOnBottomReached(e.target as HTMLDivElement)}> - + setSearchString(value)} /> diff --git a/packages/trpc/server/routers/viewer/availability/team/listTeamAvailability.handler.ts b/packages/trpc/server/routers/viewer/availability/team/listTeamAvailability.handler.ts index 0abf185b4b..52d0a7413d 100644 --- a/packages/trpc/server/routers/viewer/availability/team/listTeamAvailability.handler.ts +++ b/packages/trpc/server/routers/viewer/availability/team/listTeamAvailability.handler.ts @@ -25,18 +25,29 @@ async function getTeamMembers({ teamIds, cursor, limit, + searchString, }: { teamId?: number; organizationId: number | null; teamIds?: number[]; cursor: number | null | undefined; limit: number; + searchString?: string | null; }) { const memberships = await prisma.membership.findMany({ where: { teamId: { in: teamId ? [teamId] : teamIds, }, + ...(searchString + ? { + OR: [ + { user: { username: { contains: searchString } } }, + { user: { name: { contains: searchString } } }, + { user: { email: { contains: searchString } } }, + ], + } + : {}), }, select: { id: true, @@ -128,13 +139,22 @@ async function buildMember(member: Member, dateFrom: Dayjs, dateTo: Dayjs) { } async function getInfoForAllTeams({ ctx, input }: GetOptions) { - const { cursor, limit } = input; + const { cursor, limit, searchString } = input; // Get all teamIds for the user const teamIds = await prisma.membership .findMany({ where: { userId: ctx.user.id, + ...(searchString + ? { + OR: [ + { user: { username: { contains: searchString } } }, + { user: { name: { contains: searchString } } }, + { user: { email: { contains: searchString } } }, + ], + } + : {}), }, select: { id: true, @@ -152,6 +172,7 @@ async function getInfoForAllTeams({ ctx, input }: GetOptions) { organizationId: ctx.user.organizationId, cursor, limit, + searchString, }); // Get total team count across all teams the user is in (for pagination) @@ -169,7 +190,7 @@ async function getInfoForAllTeams({ ctx, input }: GetOptions) { } export const listTeamAvailabilityHandler = async ({ ctx, input }: GetOptions) => { - const { cursor, limit } = input; + const { cursor, limit, searchString } = input; const teamId = input.teamId || ctx.user.organizationId; let teamMembers: Member[] = []; @@ -198,6 +219,15 @@ export const listTeamAvailabilityHandler = async ({ ctx, input }: GetOptions) => totalTeamMembers = await prisma.membership.count({ where: { teamId: teamId, + ...(searchString + ? { + OR: [ + { user: { username: { contains: searchString } } }, + { user: { name: { contains: searchString } } }, + { user: { email: { contains: searchString } } }, + ], + } + : {}), }, }); @@ -207,6 +237,7 @@ export const listTeamAvailabilityHandler = async ({ ctx, input }: GetOptions) => cursor, limit, organizationId: ctx.user.organizationId, + searchString, }); } } @@ -224,11 +255,26 @@ export const listTeamAvailabilityHandler = async ({ ctx, input }: GetOptions) => const members = await Promise.all(buildMembers); + let belongsToTeam = true; + + if (totalTeamMembers === 0) { + const membership = await prisma.membership.findFirst({ + where: { + userId: ctx.user.id, + }, + select: { + id: true, + }, + }); + belongsToTeam = !!membership; + } + return { rows: members || [], nextCursor, meta: { totalRowCount: totalTeamMembers, + isApartOfAnyTeam: belongsToTeam, }, }; }; 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 e6e70c63ae..ecf26969e7 100644 --- a/packages/trpc/server/routers/viewer/availability/team/listTeamAvailability.schema.ts +++ b/packages/trpc/server/routers/viewer/availability/team/listTeamAvailability.schema.ts @@ -7,6 +7,7 @@ export const ZListTeamAvailaiblityScheme = z.object({ endDate: z.string(), loggedInUsersTz: z.string(), teamId: z.number().optional(), + searchString: z.string().toLowerCase().optional(), }); export type TListTeamAvailaiblityScheme = z.infer;